package jvDeformation;


import handler.LineHandler;
import help.DeformationFactory;
import help.GridHelper;

import java.awt.Image;
import java.awt.Menu;
import java.awt.image.BufferedImage;
import java.util.Enumeration;
import java.util.Vector;

import jv.geom.PgElementSet;
import jv.object.PsPanel;
import jv.object.PsUpdateIf;
import jv.project.PgGeometry;
import jv.project.PgGeometryIf;
import jv.project.PgJvxSrc;
import jv.project.PvDisplayIf;
import jv.project.PvGeometryIf;
import jv.project.PvPickEvent;
import jv.project.PvViewerIf;
import jv.rsrc.PsAuthorInfo;
import jv.rsrc.PsGeometryInfo;
import jv.vecmath.PdMatrix;
import jv.vecmath.PdVector;
import jv.vecmath.PiVector;
import stegru.tools.multiThreat.ParallelFor;
import deformedGrid.RectangelGridDeformation;

public class JvGrid extends RectangelGridDeformation  implements PgGeometryIf, JvDeformedImageGeometry, JvDeformationHandlerListener{

	
	private final Vector<PvDisplayIf> displays = new Vector<PvDisplayIf>();
	public final PgElementSet elementSet;
	public final BufferedImage image;
	final public int numGridPointsX;
	final public int numGridPointsY;
	private DeformationFactory deformationFactory;
	
	
	public JvGrid newInstance(){
		JvGrid newJvGrid = new JvGrid(numGridPointsX, numGridPointsY, image);
		if (this.getDeformation() != null) {
			newJvGrid.setDeformation(this.getDeformation());
		}
		newJvGrid.elementSet.showEdges(this.elementSet.isShowingEdges());
		return newJvGrid;
	}
	
	public JvGrid newInstance(int numGridPointsX, int numGridPointsY){
		JvGrid newJvGrid = new JvGrid(numGridPointsX, numGridPointsY, image);
		if (this.getDeformation() != null) {
			newJvGrid.setDeformation(this.getDeformation());
		}
		newJvGrid.elementSet.showEdges(this.elementSet.isShowingEdges());
		return newJvGrid;
	}

	
	public JvGrid newInstance(int numGridPointsX, int numGridPointsY, BufferedImage image){
		JvGrid newJvGrid = new JvGrid(numGridPointsX, numGridPointsY, image);
		if (this.getDeformation() != null) {
			newJvGrid.setDeformation(this.getDeformation());
		}		newJvGrid.elementSet.showEdges(this.elementSet.isShowingEdges());
		return newJvGrid;
	}
	
	public JvGrid newInstance(BufferedImage image){
		JvGrid newJvGrid = new JvGrid(numGridPointsX, numGridPointsY, image);
		if (this.getDeformation() != null) {
			newJvGrid.setDeformation(this.getDeformation());
		}
		newJvGrid.elementSet.showEdges(this.elementSet.isShowingEdges());
		return newJvGrid;
	}
	
	
	public void destroy() {
		for (PvDisplayIf display : displays) {
			display.removeGeometry(this);
		}
		removeDeformation();
	}
	
	
	public JvGrid(final int numGridPointsX, final int numGridPointsY, BufferedImage image) {
		super(numGridPointsX, numGridPointsY, image.getWidth(), image.getHeight());
		this.image = image;
		elementSet = new PgElementSet(2);
		elementSet.setDimOfElements(4);
		this.numGridPointsX = numGridPointsX;
		this.numGridPointsY = numGridPointsY;
		elementSet.setNumVertices(numGridPointsX * numGridPointsY);
		elementSet.setVertices(GridHelper.gridToPdVectorArray(getOriginalGrid()));
		elementSet.setVertexTextures(GridHelper.gridToScaledPdVectorArray(getOriginalGrid(), 1.0/super.width, 1.0/super.height));
		
		
		//build elements
		elementSet.setNumElements((numGridPointsY-1)*(numGridPointsX-1));
		final PiVector[] elements = new PiVector[(numGridPointsY-1)*(numGridPointsX-1)];
		(new ParallelFor(0, numGridPointsX - 2) {
			@Override
			protected void loop(int x) {
				for (int y = 0; y < numGridPointsY-1; y++) {
					elements[x + (numGridPointsX -1)* y] = new PiVector(
							getGridPointIndex(x  , y  ),
							getGridPointIndex(x+1, y  ),
							getGridPointIndex(x+1, y+1),
							getGridPointIndex(x  , y+1));
				}
			}
		}).calculate();
		elementSet.setElements(elements);
		config();
	} 
	 
	
	
	private void config(){
		elementSet.setTransparency(0);
		elementSet.setTextureImage(image);
		elementSet.showVertexTexture(true);
		elementSet.setName(this.getClass().getSimpleName());
		elementSet.showElementColors(false);
		elementSet.showVertices(false);
		elementSet.showEdges(false);
		elementSet.setCenter(new PdVector( super.width/2,super.height/2));
	}

	public void repaint(){
		for (PvDisplayIf d : displays) {
			d.update(this);
		}
	}

	
	@Override
	public void deformationChanged() {
		repaint();
	}
	

	/**
	 * @param display
	 * @return
	 * @see jv.project.PgGeometryIf#addDisplay(jv.project.PvDisplayIf)
	 */
	@Override
	public boolean addDisplay(PvDisplayIf display) {
		return displays.add(display);//elementSet.addDisplay(display);
	}

	/**
	 * @param anElement
	 * @return
	 * @see jv.project.PgGeometryIf#addElement(jv.vecmath.PiVector)
	 */
	@Override
	public int addElement(PiVector anElement) {
		throw new UnsupportedOperationException("addElement is Unsupported");
	}

	/**
	 * @param aPolygon
	 * @return
	 * @see jv.project.PgGeometryIf#addPolygon(jv.vecmath.PiVector)
	 */
	@Override
	public int addPolygon(PiVector aPolygon) {
		throw new UnsupportedOperationException("addPolygon is Unsupported");
	}

	/**
	 * @param aVertex
	 * @return
	 * @see jv.project.PgGeometryIf#addVertex(jv.vecmath.PdVector)
	 */
	@Override
	public int addVertex(PdVector aVertex) {
		throw new UnsupportedOperationException("addVertex is Unsupported");
	}

	/**
	 * @param type
	 * @param ext
	 * @return
	 * @see jv.project.PgGeometryIf#assureInspector(java.lang.String, java.lang.String)
	 */
	@Override
	public PsPanel assureInspector(String type, String ext) {
		return elementSet.assureInspector(type, ext);
	}

	/**
	 * @param s
	 * @param a
	 * @param t
	 * @param b
	 * @return
	 * @see jv.project.PgGeometryIf#blend(double, jv.project.PgGeometry, double, jv.project.PgGeometry)
	 */
	@Override
	public boolean blend(double s, PgGeometry a, double t, PgGeometry b) {
		return elementSet.blend(s, a, t, b);
	}

	/**
	 * @param index
	 * @param aTag
	 * @see jv.project.PgGeometryIf#clearTagElement(int, int)
	 */
	@Override
	public void clearTagElement(int index, int aTag) {
		elementSet.clearTagElement(index, aTag);
	}

	/**
	 * @param index
	 * @param aTag
	 * @see jv.project.PgGeometryIf#clearTagPolygon(int, int)
	 */
	@Override
	public void clearTagPolygon(int index, int aTag) {
		elementSet.clearTagPolygon(index, aTag);
	}

	/**
	 * @param index
	 * @param aTag
	 * @see jv.project.PgGeometryIf#clearTagVertex(int, int)
	 */
	@Override
	public void clearTagVertex(int index, int aTag) {
		elementSet.clearTagVertex(index, aTag);
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#clone()
	 */
	@Override
	public Object clone() {
		throw new UnsupportedOperationException();
	}

	/**
	 * @param menu
	 * @param viewer
	 * @return
	 * @see jv.project.PgGeometryIf#fillMethodMenu(java.awt.Menu, jv.project.PvViewerIf)
	 */
	@Override
	public Menu fillMethodMenu(Menu menu, PvViewerIf viewer) {
		return elementSet.fillMethodMenu(menu, viewer);
	}

	/**
	 * @param menu
	 * @return
	 * @deprecated
	 * @see jv.project.PgGeometryIf#fillMethodMenu(java.awt.Menu)
	 */
	@Deprecated
	@Override
	public Menu fillMethodMenu(Menu menu) {
		return elementSet.fillMethodMenu(menu);
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getAmbientInvMatrix()
	 */
	@Override
	public PdMatrix getAmbientInvMatrix() {
		return elementSet.getAmbientInvMatrix();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getAmbientMatrix()
	 */
	@Override
	public PdMatrix getAmbientMatrix() {
		return elementSet.getAmbientMatrix();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getAmbientProjection()
	 */
	@Override
	public int getAmbientProjection() {
		System.out.println("getAmbientProjection");
		return elementSet.getAmbientProjection();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getAmbientSpace()
	 */
	@Override
	public int getAmbientSpace() {
		return elementSet.getAmbientSpace();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getAuthorInfo()
	 */
	@Override
	public PsAuthorInfo getAuthorInfo() {
		return elementSet.getAuthorInfo();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getControlPanel()
	 */
	@Override
	public PsPanel getControlPanel() {
		return elementSet.getControlPanel();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getDimOfSimplex()
	 */
	@Override
	public int getDimOfSimplex() {
		return elementSet.getDimOfSimplex();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getDimOfVertices()
	 */
	@Override
	public int getDimOfVertices() {
		return elementSet.getDimOfVertices();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getDisplays()
	 */
	@Override
	@SuppressWarnings("rawtypes")
	public Enumeration getDisplays() {
		return displays.elements();
	}

	/**
	 * @param anIndex
	 * @return
	 * @see jv.project.PgGeometryIf#getElement(int)
	 */
	@Override
	public PiVector getElement(int anIndex) {
		return elementSet.getElement(anIndex);
	}

	/**
	 * @return
	 * @see jv.object.PsUpdateIf#getFather()
	 */
	@Override
	public PsUpdateIf getFather() {
		System.out.println("getFather");
		return elementSet.getFather();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getGeometryInfo()
	 */
	@Override
	public PsGeometryInfo getGeometryInfo() {
		return elementSet.getGeometryInfo();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getInfoPanel()
	 */
	@Override
	public PsPanel getInfoPanel() {
		return elementSet.getInfoPanel();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getJvx()
	 */
	@Override
	public PgJvxSrc getJvx() {
		return elementSet.getJvx();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getMaterialPanel()
	 */
	@Override
	public PsPanel getMaterialPanel() {
		return elementSet.getMaterialPanel();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getModelMatrix()
	 */
	@Override
	public PdMatrix getModelMatrix() {
		return elementSet.getModelMatrix();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getName()
	 */
	@Override
	public String getName() {
		return elementSet.getName();
	}

	/**
	 * @param anIndex
	 * @return
	 * @see jv.project.PgGeometryIf#getPolygon(int)
	 */
	@Override
	public PiVector getPolygon(int anIndex) {
		return elementSet.getPolygon(anIndex);
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getTextureImage()
	 */
	@Override
	public Image getTextureImage() {
		return elementSet.getTextureImage();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getTextureImageName()
	 */
	@Override
	public String getTextureImageName() {
		return elementSet.getTextureImageName();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getTitle()
	 */
	@Override
	public String getTitle() {
		return elementSet.getTitle();
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#getType()
	 */
	@Override
	public int getType() {
		return elementSet.getType();
	}

	/**
	 * @param anIndex
	 * @return
	 * @see jv.project.PgGeometryIf#getVertex(int)
	 */
	@Override
	public PdVector getVertex(int anIndex) {
		return elementSet.getVertex(anIndex);
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#hasAmbientMatrix()
	 */
	@Override
	public boolean hasAmbientMatrix() {
		return elementSet.hasAmbientMatrix();
	}

	/**
	 * @param display
	 * @return
	 * @see jv.project.PgGeometryIf#hasDisplay(jv.project.PvDisplayIf)
	 */
	@Override
	public boolean hasDisplay(PvDisplayIf display) {
		return elementSet.hasDisplay(display);
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#hasModelMatrix()
	 */
	@Override
	public boolean hasModelMatrix() {
		return elementSet.hasModelMatrix();
	}

	/**
	 * @param aBit
	 * @return
	 * @see jv.project.PgGeometryIf#hasTag(int)
	 */
	@Override
	public boolean hasTag(int aBit) {
		return elementSet.hasTag(aBit);
	}

	/**
	 * @param index
	 * @param aTag
	 * @return
	 * @see jv.project.PgGeometryIf#hasTagElement(int, int)
	 */
	@Override
	public boolean hasTagElement(int index, int aTag) {
		return elementSet.hasTagElement(index, aTag);
	}

	/**
	 * @param index
	 * @param aTag
	 * @return
	 * @see jv.project.PgGeometryIf#hasTagPolygon(int, int)
	 */
	@Override
	public boolean hasTagPolygon(int index, int aTag) {
		return elementSet.hasTagPolygon(index, aTag);
	}

	/**
	 * @param index
	 * @param aTag
	 * @return
	 * @see jv.project.PgGeometryIf#hasTagVertex(int, int)
	 */
	@Override
	public boolean hasTagVertex(int index, int aTag) {
		return elementSet.hasTagVertex(index, aTag);
	}

	/**
	 * @param base
	 * @param dir
	 * @return
	 * @see jv.project.PgGeometryIf#intersectionWithLine(jv.vecmath.PdVector, jv.vecmath.PdVector)
	 */
	@Override
	public PvPickEvent intersectionWithLine(PdVector base, PdVector dir) {
		return elementSet.intersectionWithLine(base, dir);
	}

	/**
	 * @param panel
	 * @return
	 * @see jv.project.PgGeometryIf#isConfigurable(int)
	 */
	@Override
	public boolean isConfigurable(int panel) {
		//TODO set pannel
		return elementSet.isConfigurable(panel);
	}

	/**
	 * @return
	 * @see jv.project.PgGeometryIf#isVisible()
	 */
	@Override
	public boolean isVisible() {
		return elementSet.isVisible();
	}

	/**
	 * @param geom
	 * @return
	 * @see jv.project.PgGeometryIf#merge(jv.project.PgGeometryIf)
	 */
	@Override
	public boolean merge(PgGeometryIf geom) {
		return elementSet.merge(geom);
	}

	
	
	
	
	/**
	 * @param dc
	 * @see jv.project.PgGeometryIf#paint(jv.project.PvGeometryIf)
	 */
	@Override
	public void paint(final PvGeometryIf dc) {
		elementSet.setVertices(GridHelper.gridToPdVectorArray(getGrid()));
		elementSet.paint(dc);		
		dc.setDrawingOrder(PvGeometryIf.DRAW_ORDER_BACK);
	}

	/**
	 * @param mat
	 * @param bOrient
	 * @param bVector
	 * @return
	 * @see jv.project.PgGeometryIf#reflect(jv.vecmath.PdMatrix, boolean, boolean)
	 */
	@Override
	public PgGeometry reflect(PdMatrix mat, boolean bOrient, boolean bVector) {
		return elementSet.reflect(mat, bOrient, bVector);
	}

	/**
	 * @param display
	 * @return
	 * @see jv.project.PgGeometryIf#removeDisplay(jv.project.PvDisplayIf)
	 */
	@Override
	public boolean removeDisplay(PvDisplayIf display) {
		return displays.remove(display);//elementSet.removeDisplay(display);
	}

	/**
	 * @param anIndex
	 * @return
	 * @see jv.project.PgGeometryIf#removeElement(int)
	 */
	@Override
	public int[] removeElement(int anIndex) {
		//TODO 
		return null;//elementSet.removeElement(anIndex);
	}

	/**
	 * 
	 * @see jv.project.PgGeometryIf#removeMethodMenus()
	 */
	@Override
	public void removeMethodMenus() {
		elementSet.removeMethodMenus();
	}

	/**
	 * @param anIndex
	 * @return
	 * @see jv.project.PgGeometryIf#removePolygon(int)
	 */
	@Override
	public int[] removePolygon(int anIndex) {
		return elementSet.removePolygon(anIndex);
	}

	/**
	 * @param anIndex
	 * @return
	 * @see jv.project.PgGeometryIf#removeVertex(int)
	 */
	@Override
	public int[] removeVertex(int anIndex) {
			//TODO
		return null;//elementSet.removeVertex(anIndex);
	}

	/**
	 * @param ambientMat
	 * @param ambientInvMat
	 * @see jv.project.PgGeometryIf#setAmbientMatrix(jv.vecmath.PdMatrix, jv.vecmath.PdMatrix)
	 */
	@Override
	public void setAmbientMatrix(PdMatrix ambientMat, PdMatrix ambientInvMat) {
		elementSet.setAmbientMatrix(ambientMat, ambientInvMat);
	}

	/**
	 * @param mode
	 * @see jv.project.PgGeometryIf#setAmbientProjection(int)
	 */
	@Override
	public void setAmbientProjection(int mode) {
		elementSet.setAmbientProjection(mode);
	}

	/**
	 * @param mode
	 * @see jv.project.PgGeometryIf#setAmbientSpace(int)
	 */
	@Override
	public void setAmbientSpace(int mode) {
		elementSet.setAmbientSpace(mode);
	}

	/**
	 * @param authorInfo
	 * @see jv.project.PgGeometryIf#setAuthorInfo(jv.rsrc.PsAuthorInfo)
	 */
	@Override
	public void setAuthorInfo(PsAuthorInfo authorInfo) {
		elementSet.setAuthorInfo(authorInfo);
	}

	/**
	 * @param anIndex
	 * @param anElement
	 * @return
	 * @see jv.project.PgGeometryIf#setElement(int, jv.vecmath.PiVector)
	 */
	@Override
	public boolean setElement(int anIndex, PiVector anElement) {
		//TODO
		return false;// elementSet.setElement(anIndex, anElement);
	}

	/**
	 * @param geometryInfo
	 * @see jv.project.PgGeometryIf#setGeometryInfo(jv.rsrc.PsGeometryInfo)
	 */
	@Override
	public void setGeometryInfo(PsGeometryInfo geometryInfo) {
		elementSet.setGeometryInfo(geometryInfo);
	}

	/**
	 * @param src
	 * @see jv.project.PgGeometryIf#setJvx(jv.project.PgJvxSrc)
	 */
	@Override
	public void setJvx(PgJvxSrc src) {
		elementSet.setJvx(src);
	}

	/**
	 * @param modelMat
	 * @see jv.project.PgGeometryIf#setModelMatrix(jv.vecmath.PdMatrix)
	 */
	@Override
	public void setModelMatrix(PdMatrix modelMat) {
		elementSet.setModelMatrix(modelMat);
	}

	/**
	 * @param name
	 * @see jv.project.PgGeometryIf#setName(java.lang.String)
	 */
	@Override
	public void setName(String name) {
		elementSet.setName(name);
	}

	/**
	 * @param parent
	 * @see jv.object.PsUpdateIf#setParent(jv.object.PsUpdateIf)
	 */
	@Override
	public void setParent(PsUpdateIf parent) {
		elementSet.setParent(parent);
	}

	/**
	 * @param anIndex
	 * @param aPolygon
	 * @return
	 * @see jv.project.PgGeometryIf#setPolygon(int, jv.vecmath.PiVector)
	 */
	@Override
	public boolean setPolygon(int anIndex, PiVector aPolygon) {
		return elementSet.setPolygon(anIndex, aPolygon);
	}

	/**
	 * @param key
	 * @param state
	 * @see jv.project.PgGeometryIf#setState(int, boolean)
	 */
	@Override
	public void setState(int key, boolean state) {
		elementSet.setState(key, state);
	}

	/**
	 * @param index
	 * @param aTag
	 * @see jv.project.PgGeometryIf#setTagElement(int, int)
	 */
	@Override
	public void setTagElement(int index, int aTag) {
		elementSet.setTagElement(index, aTag);
	}

	/**
	 * @param index
	 * @param aTag
	 * @see jv.project.PgGeometryIf#setTagPolygon(int, int)
	 */
	@Override
	public void setTagPolygon(int index, int aTag) {
		elementSet.setTagPolygon(index, aTag);
	}

	/**
	 * @param index
	 * @param aTag
	 * @see jv.project.PgGeometryIf#setTagVertex(int, int)
	 */
	@Override
	public void setTagVertex(int index, int aTag) {
		elementSet.setTagVertex(index, aTag);
	}

	/**
	 * @param img
	 * @see jv.project.PgGeometryIf#setTextureImage(java.awt.Image)
	 */
	@Override
	public void setTextureImage(Image img) {
		elementSet.setTextureImage(img);
	}

	/**
	 * @param name
	 * @see jv.project.PgGeometryIf#setTextureImageName(java.lang.String)
	 */
	@Override
	public void setTextureImageName(String name) {
		elementSet.setTextureImageName(name);
	}

	/**
	 * @param title
	 * @see jv.project.PgGeometryIf#setTitle(java.lang.String)
	 */
	@Override
	public void setTitle(String title) {
		elementSet.setTitle(title);
	}

	/**
	 * @param anIndex
	 * @param aVertex
	 * @return
	 * @see jv.project.PgGeometryIf#setVertex(int, jv.vecmath.PdVector)
	 */
	@Override
	public boolean setVertex(int anIndex, PdVector aVertex) {
		return false;//elementSet.setVertex(anIndex, aVertex);
	}

	/**
	 * @param flag
	 * @see jv.project.PgGeometryIf#setVisible(boolean)
	 */
	@Override
	public void setVisible(boolean flag) {
		elementSet.setVisible(flag);
	}


	/**
	 * @param event
	 * @return
	 * @see jv.object.PsUpdateIf#update(java.lang.Object)
	 */
	@Override
	public boolean update(Object event) {
		if (event.getClass() == this.getClass()) {
			repaint();
			return true;
		} else {
			return false;
		}
	}

	@Override
	public PgGeometryIf getGeometry() {
		return this;
	}

	
	
	private LineHandler handler;
	@Override
	public void stickPoints(LineHandler handler) {
		if (deformationFactory != null) {
			setDeformation(deformationFactory.generate(handler));
		}
		
		this.handler = handler;
	}

	@Override
	public void unstick() {
		removeDeformation();
		handler = null;
	}

	public DeformationFactory getDeformationFactory() {
		return deformationFactory;
	}

	public void setDeformationFactory(DeformationFactory deformationFactory) {
		this.deformationFactory = deformationFactory;
		if (this.handler != null) {
			setDeformation(deformationFactory.generate(handler));
		}
	}

}
